#ifndef _COMM_HPP_
#define _COMM_HPP_

#include<iostream>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<cerrno>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<unistd.h>

#define PATHNAME "."
#define PROJ_ID 0x66
//共享内存的大小，一般建议是4KB的整数倍
//系统分配共享内存是以4KB为单位的-------内存划分内存块的基本单位
#define MAX_SIZE 4096
//#define MAX_SIZE 4097//内核给你的会向上取整，但是ipcs -m 查看的时候还是4097
//是因为内核给你的和你能用的是两码事，你只能用4097

key_t getKey()
{
    key_t k = ftok(PATHNAME,PROJ_ID);
    if(k < 0)
    {
        std::cerr<<errno<<":"<<strerror(errno)<<std::endl;
        exit(1);
    }
    return k;
}

int getShmHelper(key_t k,int flags)
{
    //k是要shmget设置进入共享内存属性中的用来标识该共享内存在内核中的唯一性
    //shmid  vs  key
    //fd     vs  inode
    //关系类似于上面这两个例子
    int shmid = shmget(k,MAX_SIZE,flags);
    if(shmid < 0)
    {
        std::cerr<<errno<<":"<<strerror(errno)<<std::endl;
        exit(2);
    }
    return shmid;
}

int getShm(key_t k)
{
    return getShmHelper(k,IPC_CREAT);
}

int createShm(key_t k)
{
    return getShmHelper(k,IPC_CREAT | IPC_EXCL | 0600);//不加上0600的话shmat那会报错
}

void delShm(int shmid)
{
    if(shmctl(shmid,IPC_RMID,nullptr) == -1)
    {
        std::cerr<<errno<<":"<<strerror(errno)<<std::endl;
    }
}

void* attachShm(int shmid)
{
    void* mem = shmat(shmid,nullptr,0);//0的话表示可读可写
    if((long long)mem == -1L)//64位系统指针大小8字节所以转化为int会精度丢失
    {
        std::cerr<<"shmat : "<<errno<<":"<<strerror(errno)<<std::endl;
        exit(3);
    }
    return mem;
}

void detachShm(void* start)
{
    if(shmdt(start) == -1)
    {
        std::cerr<<"shmdt : "<<errno<<":"<<strerror(errno)<<std::endl;
        exit(4);
    }
}

//同样的代码，考虑键盘输入和显示器输出，共有几次数据拷贝
//管道：从键盘输入到buffer中会有两次,FILE*一次，再从FILE*到buffer一次，write调用会涉及到
//内核级缓冲区一次，数据流向管道，read从管道读取涉及到内核级缓冲区一次，同理输出显示器2次，read
//读到buffer中一次，buffer到显示器会经历FILE*一次所以4+2=6次
//共享内存：输入到共享内存里1次，从共享内存里读走1次，如果再考虑上FILE*就是2+2=4次
#endif